/*******************************************************************************
 * @file fdt_daemon.c
 * @brief  inter process Fast Data Transfer
 *
 *
 * Author: Kevinx
 *
 * Copyright (c) 2015 Kevinx. All Rights Reserved.
 *
 * CONFIDENTIALITY AND LIMITED USE
 *
 * This software, including any software of third parties embodied herein,
 * contains information and concepts which are confidential to Kevinx
 * and such third parties. This software is licensed for use
 * solely in accordance with the terms and conditions of the applicable
 * license agreement with Kevinx or its authorized distributor.
 ************************************************************************************/
#include "fdt_daemon.h"
#include <errno.h>

static void add_fd(struct fdt_daemon* dm, int fd)
{
    FD_SET(fd, &dm->sop.rset);
    dm->sop.nr_fd ++;

    if (fd > dm->sop.maxfd)
        dm->sop.maxfd = fd;
}

static void del_fd(struct fdt_daemon* dm, int fd)
{
    if (FD_ISSET(fd, &dm->sop.rset)) {
        FD_CLR(fd, &dm->sop.rset);
        dm->sop.nr_fd --;
    }
}

/*
 * fdt_PTR_channel:
 *   test if c is a PTR channel
 *
 */
static inline int PTR_channel(struct fdt_channel* c)
{
    return (c->flags & FCH_TYPE_PTR);
}

/*
 * fdt_daemon_init
 *  init fdt_daemon struct
 *
 */
void fdt_daemon_init(struct fdt_daemon* dm)
{
    dm->sop.maxfd = -1;
    dm->sop.nr_fd = 0;
    FD_ZERO(&dm->sop.rset);
    INIT_LIST_HEAD(&dm->list);
    INIT_LIST_HEAD(&dm->fd_list);
    INIT_LIST_HEAD(&dm->sock_list);
    dm->nr_ch = 0;
    dm->nr_fd = 0;
    dm->nr_sock= 0;
}

void fdt_daemon_add_channel(struct fdt_daemon* dm, struct fdt_channel* c, struct fdt_callback* cb)
{
    c->cb = cb;
    add_fd(dm, *c->ifd);
    add_fd(dm, *c->gc_fd);
    list_add(&c->list, &dm->list);
    dm->nr_ch ++;
}

void fdt_daemon_del_channel(struct fdt_daemon* dm, struct fdt_channel* c)
{
    del_fd(dm, *c->ifd);
    del_fd(dm, *c->gc_fd);
    list_del(&c->list);
    dm->nr_ch --;    
}

void fdt_daemon_add_fdesc(struct fdt_daemon* dm, struct fdt_fdesc* fdesc)
{
    add_fd(dm, fdesc->fd);
    list_add(&fdesc->list, &dm->fd_list);
    dm->nr_fd ++;
}

void fdt_daemon_del_fdesc(struct fdt_daemon* dm, struct fdt_fdesc* fdesc)
{
    del_fd(dm, fdesc->fd);
    list_del(&fdesc->list);
    dm->nr_fd --;
}

void fdt_daemon_add_socket(struct fdt_daemon* dm, struct fdt_socket* sock, struct fdt_sock_cb* cb)
{
    sock->cb = cb;
    add_fd(dm, sock->fd);
    list_add(&sock->list, &dm->sock_list);
    dm->nr_sock ++;
}

void fdt_daemon_del_socket(struct fdt_daemon* dm, struct fdt_socket* sock)
{
    del_fd(dm, sock->fd);
    list_del(&sock->list);
    dm->nr_sock --;    
}

int fdt_daemon_start(struct fdt_daemon* dm)
{
    fd_set rfds;
    int ret;
    struct fdt_channel* pos;
    struct fdt_fdesc* fdesc;
    struct fdt_socket* sock;

    for (;;) {
        FD_ZERO(&rfds);
        rfds = dm->sop.rset;

        do {
            ret = select(dm->sop.maxfd + 1, &rfds, 0, 0, NULL); 
        } while (ret < 0 && errno == EINTR);  

        list_for_each_entry(pos, &dm->list, list) {
            if (FD_ISSET(*pos->ifd, &rfds)) {
                if (pos->cb != NULL && pos->cb->func != NULL)
                    pos->cb->func(pos);
            }

            if (PTR_channel(pos) && FD_ISSET(*pos->gc_fd, &rfds)) {
                fdt_PTRch_gcollect(pos);
            }
        }  

        list_for_each_entry(fdesc, &dm->fd_list, list) {
            if (FD_ISSET(fdesc->fd, &rfds)) {
                if (fdesc->action != NULL)
                    fdesc->action(fdesc->fd, fdesc->private_data);

            }
        }  

        list_for_each_entry(sock, &dm->sock_list, list) {
            if (FD_ISSET(sock->fd, &rfds)) {
                if (sock->cb != NULL && sock->cb->func != NULL)
                    sock->cb->func(sock);

            }
        }         
    }

    return 0;
}

